package com.usvisatrack.core.common;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.routines.checkdigit.CheckDigitException;
import org.apache.commons.validator.routines.checkdigit.EAN13CheckDigit;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.filter.Filters;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;


import ij.IJ;
import ij.ImagePlus;
import ij.process.ImageProcessor;

/**
 * BcoDepot BcodePotUtils class
 * 
 * @author YuCheng Hu
 *
 */
public final class USVisaTrackUtils {

	private static final Logger logger = LoggerFactory.getLogger(USVisaTrackUtils.class);

	private static ObjectMapper objectMapp = new ObjectMapper();

	// log specificly for melissa data calls
	// private static Logger mdLog =
	// LoggerFactory.getLogger("com.verani.BcodePotUtils.melissaData");
	// private static Pattern emailPattern = Pattern
	// .compile("^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$");

	public static String createCacheFile(String fileName) {

		Date currentDate = new Date();

		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");

		return "/" + yyyyMM.format(currentDate) + "/" + dd.format(currentDate) + "/" + currentDate.getTime() + "/" + currentDate.getTime()
				+ fileName;

	}

	public static String createCacheFile(String folderPath, String fileName) {

		Date currentDate = new Date();

		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");

		return folderPath + "/" + yyyyMM.format(currentDate) + "/" + dd.format(currentDate) + "/" + currentDate.getTime() + fileName;

	}

	public static void toJson(HttpServletResponse response, String contentType, Object value) {
		Assert.notNull(response);
		Assert.hasText(contentType);
		try {
			response.setContentType(contentType);
			objectMapp.writeValue(response.getWriter(), value);
		} catch (Exception localException) {
			localException.printStackTrace();
		}
	}

	public static void toJson(HttpServletResponse response, Object value) {
		Assert.notNull(response);
		PrintWriter localPrintWriter = null;
		try {
			localPrintWriter = response.getWriter();
			objectMapp.writeValue(localPrintWriter, value);
			localPrintWriter.flush();
		} catch (Exception localException) {
			localException.printStackTrace();
		} finally {
			IOUtils.closeQuietly(localPrintWriter);
		}
	}

	public static <T> T toObject(String json, Class<T> valueType) {
		Assert.hasText(json);
		Assert.notNull(valueType);
		try {
			return objectMapp.readValue(json, valueType);
		} catch (Exception localException) {
			localException.printStackTrace();
		}
		return null;
	}

	public static <T> T toObject(String json, TypeReference<?> typeReference) {
		Assert.hasText(json);
		Assert.notNull(typeReference);
		try {
			return objectMapp.readValue(json, typeReference);
		} catch (Exception localException) {
			localException.printStackTrace();
		}
		return null;
	}

	public static <T> T toObject(String json, JavaType javaType) {
		Assert.hasText(json);
		Assert.notNull(javaType);
		try {
			return objectMapp.readValue(json, javaType);
		} catch (Exception localException) {
			localException.printStackTrace();
		}
		return null;
	}

	public static String generateShortUUID() {
		UUID uuid = UUID.randomUUID();
		logger.debug("UUID: " + uuid);
		// trim UUID
		char[] uuidChars = uuid.toString().toCharArray();
		String newUUID = "";
		for (int i = 0; i < 8; i++) {
			newUUID += uuidChars[i];
		}
		logger.debug("Trimmed UUID: " + newUUID);
		return newUUID;
	}

	/**
	 * This method is handy for cleaning the street address of a listing (for URL). Many of our listings do not have street addresses or
	 * parts of a street address. Rather than duplicate this code in several places, I have just created a BcodePotUtils method to do the
	 * cleaning. The method will check for nulls and do REGEX replaces to remove some things. First, any non-numeric or non-alphanumeric
	 * characters will be replaced with URL friendly "-" character. If multiple "--" characters occur... they will be replaced. Also, if the
	 * string has a trailing "-" remove that so it doesn't look funny on URL.
	 * 
	 * @param streetNumber
	 *            - Listing Street Number (maybe null)
	 * @param streetName
	 *            - Listing Street Name (maybe null)
	 * @param separator
	 *            - Character to separate Street Number and Street Name (will use space " " if null passes)
	 * @return
	 */
	public static String filterStreetAddressURL(String streetNumber, String streetName, String separator) {
		if (streetNumber != null && streetName != null) {
			streetNumber = streetNumber.trim().replaceAll("[^a-zA-Z0-9]", "-");
			streetName = streetName.trim().replaceAll("[^a-zA-Z0-9]", "-");
			separator = (separator != null && !separator.isEmpty() ? separator : "-");
			return (streetNumber + separator + streetName).replaceAll("[-]{2,10}", "-").replaceAll("[-]{1,10}$", "");
		} else if (streetNumber != null) {
			return streetNumber.trim().replaceAll("[-]{2,10}", "-").replaceAll("[-]{1,10}$", "");
		} else if (streetName != null) {
			return streetName.trim().replaceAll("[-]{2,10}", "-").replaceAll("[-]{1,10}$", "");
		} else
			return "";
	}

	/**
	 * Override the previous method. Allow for simpler functionality.
	 * 
	 * @param streetNumber
	 * @param streetName
	 * @return
	 */
	public static String filterStreetAddressURL(String streetNumber, String streetName) {
		return filterStreetAddressURL(streetNumber, streetName, null);
	}

	/**
	 * Parses an integer from the specified string.
	 * 
	 * @param string
	 * @return
	 */
	public static int parseInt(String string) {
		if (string == null)
			return 0;

		try {
			return Integer.parseInt(string);
		} catch (NumberFormatException ex) {
			return 0;
		}
	}

	/**
	 * Parses an integer from an object.
	 * 
	 * @param input
	 * @return
	 */
	public static int parseInt(Object input) {
		if (input != null) {
			// is it an int already?
			if (input instanceof Integer)
				return (Integer) input;

			// string?
			if (input instanceof String)
				return USVisaTrackUtils.parseInt((String) input);
		}

		return 0;
	}

	/**
	 * Determines if the string is empty.
	 * 
	 * @param string
	 * @return
	 */
	public static boolean isEmpty(String string) {
		return string == null || string.equals("");
	}

	/**
	 * Joins a iterable string list into a string with the specified delimeter.
	 * 
	 * @param list
	 * @param delimeter
	 * @return
	 */
	public static String join(String[] list, String delimeter) {
		ArrayList<String> arrayList = new ArrayList<String>();
		for (String entry : list) {
			arrayList.add(entry);
		}
		return USVisaTrackUtils.join(arrayList, delimeter);
	}

	/**
	 * Joins a iterable string list into a string with the specified delimeter.
	 * 
	 * @param list
	 * @param delimeter
	 * @return
	 */
	public static String join(Iterable<String> list, String delimeter) {
		StringBuffer buffer = new StringBuffer();
		Iterator<String> iterator = list.iterator();

		while (iterator.hasNext()) {
			buffer.append(iterator.next());

			if (iterator.hasNext())
				buffer.append(delimeter);
		}

		return buffer.toString();
	}

	/**
	 * Creates a new {@link ArrayList} from the specified array.
	 * 
	 * @param <T>
	 * @param array
	 * @return
	 */
	public static <T> List<T> toList(T[] array) {
		List<T> result = new ArrayList<T>();

		for (T entry : array) {
			result.add(entry);
		}

		return result;
	}

	/**
	 * Joins a iterable object list into a string with the specified delimeter. toString is called on each object.
	 * 
	 * @param list
	 * @param delimeter
	 * @return
	 */
	public static <T> String joinObjects(Iterable<T> list, String delimeter) {
		StringBuffer buffer = new StringBuffer();
		Iterator<T> iterator = list.iterator();

		while (iterator.hasNext()) {
			buffer.append(iterator.next());

			if (iterator.hasNext())
				buffer.append(delimeter);
		}

		return buffer.toString();
	}

	/**
	 * Parses a boolean from a string.
	 * 
	 * @param value
	 * @return
	 */
	public static boolean parseBoolean(String value) {
		if (value != null && value.toLowerCase().equals("true"))
			return true;

		return false;
	}

	/**
	 * Parses a boolean from an object.
	 * 
	 * @param value
	 * @return
	 */
	public static boolean parseBoolean(Object value) {
		if (value == null)
			return false;

		if (value instanceof String)
			return USVisaTrackUtils.equals(((String) value).toLowerCase(), "true");

		if (value instanceof Boolean)
			return (Boolean) value;

		return false;
	}

	/**
	 * Parses a float from a string.
	 * 
	 * @param value
	 * @return
	 */
	public static float parseFloat(String value) {

		if (value != null) {
			try {
				return Float.parseFloat(value);
			} catch (NumberFormatException ex) {
				return 0.0f;
			}
		}

		return 0.0f;
	}

	/**
	 * Parses a float from a string.
	 * 
	 * @param value
	 * @return
	 */
	public static double parseDouble(String value) {

		if (value != null) {
			try {
				return Double.parseDouble(value);
			} catch (NumberFormatException ex) {
				return 0.0d;
			}
		}

		return 0.0d;
	}

	/**
	 * Performs a null safe equality check on the two provided objects.
	 * 
	 * @param a
	 * @param b
	 * @return
	 */
	public static boolean equals(Object a, Object b) {
		// both null, equal
		if (a == b)
			return true;

		// one is null and the other is not, not equal
		if ((a == null && b != null) || (a != null && b == null))
			return false;

		// neither are null, compare them
		return a.equals(b);
	}

	/**
	 * Creates a url friendly version of the string.
	 * 
	 * @param name
	 * @return
	 */
	public static String makeUrlFriendly(String name) {
		// Compile regular expression
		Pattern pattern = Pattern.compile("[^A-Za-z0-9\\s]");
		String output = pattern.matcher(name).replaceAll("");

		return output.replace(" ", "-").toLowerCase();
	}

	/**
	 * Ensures the uri ends with a trailing /
	 * 
	 * @param value
	 * @return
	 */
	public static String uriTrailingSeparator(String value) {
		if (value.endsWith("/"))
			return value;
		else
			return value + "/";
	}

	/**
	 * Ensures the uri starts with a /
	 * 
	 * @param value
	 * @return
	 */
	public static String uriPrecedingSeparator(String value) {
		if (value != null) {
			if (value.startsWith("/"))
				return value;
			else
				return "/" + value;
		}

		return "";
	}

	/**
	 * Ensures the value has both a preceding and trailing separator.
	 * 
	 * @param value
	 * @return
	 */
	public static String uriWrapSeparator(String value) {
		return USVisaTrackUtils.uriPrecedingSeparator(USVisaTrackUtils.uriTrailingSeparator(value));
	}

	/**
	 * Ensures the uri does not end with a separator.
	 * 
	 * @param fullUrl
	 * @return
	 */
	public static String uriRemoveTrailingSeparator(String fullUrl) {
		return fullUrl.endsWith("/") ? fullUrl.substring(0, fullUrl.length() - 1) : fullUrl;
	}

	/**
	 * Pads a number with preceeding zeros.
	 * 
	 * @param object
	 *            the number to pad.
	 * @param padding
	 *            the number of zeros to pad.
	 * @return
	 */
	public static String zeroPad(Object object, int padding) {
		String rawFloat = object.toString();
		int dotIndex = rawFloat.indexOf(".");
		if (dotIndex == -1)
			dotIndex = rawFloat.length();
		// if (dotIndex > padding)
		// throw new IllegalArgumentException("Try to pad on a number too big");
		StringBuilder paddedFloat = new StringBuilder();
		for (int padIndex = dotIndex; padIndex < padding; padIndex++) {
			paddedFloat.append('0');
		}
		return paddedFloat.append(rawFloat).toString();
	}

	/**
	 * Converts an input stream to string data.
	 * 
	 * @param in
	 * @return
	 */
	public static String readString(InputStream in) {
		StringBuffer out = new StringBuffer();
		byte[] b = new byte[4096];
		try {
			for (int n; (n = in.read(b)) != -1;) {
				out.append(new String(b, 0, n));
			}
		} catch (IOException ex) {
		}
		return out.toString();
	}

	/**
	 * Gets the full state name based on the short state name.
	 * 
	 * @param shortStateName
	 * @return
	 */
	public static String getFullState(String shortStateName) {
		if (shortStateName.equalsIgnoreCase("NH"))
			return "New Hampshire";
		else if (shortStateName.equalsIgnoreCase("MA"))
			return "Massachusetts";
		else if (shortStateName.equalsIgnoreCase("ME"))
			return "Maine";
		else if (shortStateName.equalsIgnoreCase("VT"))
			return "Vermont";
		else if (shortStateName.equalsIgnoreCase("CT"))
			return "Connecticut";
		else if (shortStateName.equalsIgnoreCase("RI"))
			return "Rhode Island";
		else if (shortStateName.equalsIgnoreCase("NY"))
			return "New York";

		return "";
	}

	/*
	 * return StringBuilder and/or make Writer param and write to stream directly
	 */
	public static String htmlEncode(String s) {
		StringBuilder buf = new StringBuilder(s.length());
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') {
				buf.append(c);
			} else {
				buf.append("&#").append((int) c).append(";");
			}
		}
		return buf.toString();
	}

	/**
	 * Gets a byte array from the specified input stream.
	 * 
	 * @param input
	 * @return
	 */
	public static byte[] readStream(InputStream input) {
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		int buffer = 0;

		try {
			while ((buffer = input.read()) > -1) {
				stream.write(buffer);
			}
		} catch (IOException e) {
			USVisaTrackUtils.logger.error("Error converting input stream", e);
		}

		return stream.toByteArray();
	}

	/**
	 * Gets a charset for the specified charset name.
	 * 
	 * @param charset
	 * @return
	 */
	public static Charset getCharset(String charset) {
		try {
			return Charset.forName(charset);
		} catch (Exception ex) {
			USVisaTrackUtils.logger.warn("Charset not found: " + charset, ex);
		}

		return null;
	}

	/**
	 * Writes the specified data to a file specified by the fileName parameter.
	 * 
	 * @param fileName
	 * @param byteArray
	 */
	public static void writeFile(String fileName, byte[] data) throws IOException {
		FileOutputStream output = new FileOutputStream(fileName);
		output.write(data);
		output.close();
	}

	/**
	 * Deletes a directory, even if it contains files.
	 * 
	 * @param path
	 * @return
	 */
	public static boolean deleteDir(File path) {
		if (path.exists()) {
			File[] files = path.listFiles();
			for (int i = 0; i < files.length; i++) {
				if (files[i].isDirectory()) {
					USVisaTrackUtils.deleteDir(files[i]);
				} else {
					files[i].delete();
				}
			}
		}

		return path.delete();
	}

	/**
	 * Determines whether or not the string is in the specified array. NOTE: This is not intended to be used with large arrays.
	 * 
	 * @param array
	 * @param value
	 * @return
	 */
	public static boolean contains(String[] array, String value) {
		for (String string : array) {
			if (USVisaTrackUtils.equals(string, value))
				return true;
		}

		return false;
	}

	/**
	 * Convertes a stack trace to string.
	 * 
	 * @param stackTrace
	 * @return
	 */
	public static String getStackTrace(StackTraceElement[] stackTrace) {
		StringBuffer buffer = new StringBuffer();

		for (StackTraceElement element : stackTrace) {
			buffer.append("\tat " + element.toString() + "\n");
		}

		return buffer.toString();
	}

	/**
	 * Formats an exception for logging.
	 * 
	 * @param ex
	 * @return
	 */
	public static String formatException(Throwable ex) {
		return ex.getMessage() + "\n" + USVisaTrackUtils.getStackTrace(ex.getStackTrace());
	}

	/**
	 * Determines whether the first date is before the second. If inclusive is true, then if they are equal it will also return true. This
	 * 
	 * @param a
	 * @param b
	 * @param inclusive
	 * @return
	 */
	public static boolean before(Date a, Date b, boolean inclusive) {
		if (a == null)
			return true;

		if (b == null)
			return false;

		return a.before(b) || (inclusive && a.equals(b));
	}

	/**
	 * Rounds a decimal value to the specified number of places.
	 * 
	 * @param number
	 * @param places
	 * @return
	 */
	public static double round(double number, int places) {
		double power = Math.pow(10, places);

		return (double) (Math.round(number * (power)) / power);
	}

	/**
	 * Rounds a decimal value to the specified number of places.
	 * 
	 * @param number
	 * @param places
	 * @return
	 */
	public static double round(Float number, int places) {
		return round((double) number, places);
	}

	/**
	 * Format the String to clean HTML tag.
	 * 
	 * @param input
	 * @return
	 */
	public static String cleanHtmlTag(String input) {
		if (input == null || input.trim().equals("")) {
			return "";
		}

		String str = input.replaceAll("\\&[a-zA-Z]{1,10};", "").replaceAll("<[^>]*>", "");
		str = str.replaceAll("[(/>)<]", "");

		return str;
	}

	/**
	 * Parss HTML to TEXT String
	 * 
	 * @param inputString
	 * @return
	 */
	public static String Html2Text(String inputString) {
		String htmlStr = inputString; //
		String textStr = "";

		Pattern p_script;
		Matcher m_script;

		Pattern p_style;
		Matcher m_style;

		Pattern p_html;
		Matcher m_html;

		try {
			String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>"; // script
			// Regex
			// {<script[^>]*?>[\\s\\S]*?<\\/script>
			// }
			String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>"; // style
			// Regex{<style[^>]*?>[\\s\\S]*?<\\/style>
			// }
			String regEx_html = "<[^>]+>"; // HTML Regex

			p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
			m_script = p_script.matcher(htmlStr);
			htmlStr = m_script.replaceAll(""); // parse Script tag

			p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
			m_style = p_style.matcher(htmlStr);
			htmlStr = m_style.replaceAll(""); // Parse Style tag

			p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
			m_html = p_html.matcher(htmlStr);
			htmlStr = m_html.replaceAll(""); // Parse Html tag

			textStr = htmlStr;

		} catch (Exception e) {
			System.err.println("Html2Text: " + e.getMessage());
		}

		return textStr;// retrun String
	}

	/**
	 * Check a listing in Polygon or Not
	 * 
	 * @param points
	 * @param lat
	 * @param lon
	 * @return
	 */
	public static Boolean checkPointInPolygon(double[][] points, double lat, double lon) {
		int i;
		int j = points.length - 1;
		Boolean inPoly = false;

		// System.out.println("+A+" +
		// points[i][0]+(lon-points[i][1])/(points[j][1]-points[i][1])*(points[j][0]-points[i][0]));

		for (i = 0; i < points.length; i++) {
			if (points[i][1] < lon && points[j][1] >= lon || points[j][1] < lon && points[i][1] >= lon) {
				if ((points[i][0] + (lon - points[i][1]) / (points[j][1] - points[i][1]) * (points[j][0] - points[i][0])) < lat) {
					inPoly = !inPoly;
				}
			}
			j = i;
		}
		return inPoly;
	}

	/**
	 * Get MD5 Hash
	 * 
	 * @param origin
	 * @return
	 */
	public static String getMD5Encode(String origin) {

		StringBuffer sb = new StringBuffer();
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");

			md.update(origin.getBytes());

			Hasher hasher = Hashing.md5().newHasher();
			hasher.putString(origin, Charsets.UTF_8);
			byte[] mdbytes = hasher.hash().asBytes();

			// convert the byte to hex format method 1

			for (int i = 0; i < mdbytes.length; i++) {
				sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
			}
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return sb.toString();
	}

	/**
	 * Short URL for a long String
	 * 
	 * @param key
	 * @param longUrl
	 * @return
	 */
	public static String[] getShortUrl(String key, String longUrl) {
		String[] chars = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
				"u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
				"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };

		String hex = getMD5Encode(key + longUrl);
		int hexLen = hex.length();
		int subHexLen = hexLen / 8;
		String[] ShortStr = new String[4];

		for (int i = 0; i < subHexLen; i++) {
			String outChars = "";
			int j = i + 1;
			String subHex = hex.substring(i * 8, j * 8);
			long idx = Long.valueOf("3FFFFFFF", 16) & Long.valueOf(subHex, 16);

			for (int k = 0; k < 6; k++) {
				int index = (int) (Long.valueOf("0000003D", 16) & idx);
				outChars += chars[index];
				idx = idx >> 5;
			}
			ShortStr[i] = outChars;
		}

		return ShortStr;

	}



	/**
	 * Replace url from HTTP to HTTPS
	 * 
	 * 
	 * @param url
	 * @return
	 */
	public static String httpsUrl(String url) {
		if (StringUtils.startsWithIgnoreCase(url, "http")) {
			return StringUtils.replace(url, "http", "https");
		} else
			return url;

	}

	/**
	 * Log Data to DISK.
	 * 
	 * @param logFilesPath
	 * @param logFilesName
	 * @param contentStream
	 * @throws IOException
	 */
	public static void logData(String logFilesPath, String logFilesName, OutputStream contentStream) throws IOException {
		Date currentDate = new Date();

		// Create a folder for files.
		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");
		logFilesPath = logFilesPath + "/" + yyyyMM.format(currentDate) + "/" + dd.format(currentDate) + "/" + currentDate.getTime() + "/";

		// Get file Obj by Path.
		File logFile = new File(logFilesPath + logFilesName);

		byte[] data = ((ByteArrayOutputStream) contentStream).toByteArray();
		FileUtils.writeByteArrayToFile(logFile, data);

	}

	/**
	 * 
	 * @param cDate
	 * @return
	 */
	public static String getCDNAttachmentName(File attachmentFile, Date cDate) {

		String filePath = null;

		// Create a folder for files.
		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");
		filePath = yyyyMM.format(cDate) + "/" + dd.format(cDate) + "/" + cDate.getTime();

		filePath = filePath + "_" + RandomStringUtils.random(12, true, true) + "." + FilenameUtils.getExtension(attachmentFile.getName());
		filePath = StringUtils.lowerCase(filePath);

		return filePath;

	}

	/**
	 * Wirte String to logFile
	 * 
	 * @param logFilesPath
	 * @param logFilesName
	 * @param content
	 * @throws IOException
	 */
	public static void logData(String logFilesPath, String logFilesName, String content) throws IOException {
		Date currentDate = new Date();

		// Create a folder for files.
		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");
		logFilesPath = logFilesPath + "/" + yyyyMM.format(currentDate) + "/" + dd.format(currentDate) + "/" + currentDate.getTime() + "/";

		// Get file Obj by Path.
		File logFile = new File(logFilesPath + logFilesName);

		FileUtils.writeStringToFile(logFile, content);
	}

	/**
	 * Save multiple files as the same time
	 * 
	 * 
	 * @param logFilesPath
	 * @param contentStreamMap
	 * @throws IOException
	 */
	public static void logData(String logFilesPath, HashMap<String, OutputStream> contentStreamMap) throws IOException {
		Date currentDate = new Date();

		// Create a folder for files.
		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");
		logFilesPath = logFilesPath + "/" + yyyyMM.format(currentDate) + "/" + dd.format(currentDate) + "/" + currentDate.getTime() + "/";

		// Get file Obj by Path.
		Iterator<Map.Entry<String, OutputStream>> iter = contentStreamMap.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry<String, OutputStream> entry = iter.next();
			String key = (String) entry.getKey();
			OutputStream contentStream = (OutputStream) entry.getValue();

			File logFile = new File(logFilesPath + key);

			byte[] data = ((ByteArrayOutputStream) contentStream).toByteArray();
			FileUtils.writeByteArrayToFile(logFile, data);

		}

	}

	/**
	 * Save Search OBJ to DISK and CDN
	 * 
	 * @param cDate
	 * @param searchId
	 * @param criteria
	 * @throws IOException
	 */

	/**
	 * 
	 * @param cDate
	 * @return
	 */
	public static String getDateFolderFormat(Date cDate) {

		String folderString = "";

		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");
		folderString = folderString + "/" + yyyyMM.format(cDate) + "/" + dd.format(cDate) + "/";

		return folderString;
	}

	/**
	 * Get Date folder
	 * 
	 * @param cDate
	 * @return
	 */
	public static String getFolderDated(DateTime cDate) {
		return cDate.toString("/yyyyMM/dd/");
	}

	/**
	 * 
	 * @param cDate
	 * @return
	 */
	public static String getFolderDated(Date cDate, Integer level) {
		String folderString = "";
		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		DateFormat dd = new SimpleDateFormat("dd");

		if (level.equals(1)) {
			folderString = folderString + "/" + yyyyMM.format(cDate) + "/";
		} else {
			folderString = folderString + "/" + yyyyMM.format(cDate) + "/" + dd.format(cDate) + "/";
		}

		return folderString;
	}

	/**
	 * 
	 * @param xPathStr
	 * @param document
	 * @return
	 */
	public static String getXMLValueByxPath(String xPathStr, Document document) {
		XPathExpression<Element> xPath = XPathFactory.instance().compile(xPathStr, Filters.element());
		Element dealsDataElement = xPath.evaluateFirst(document);

		if (dealsDataElement != null) {
			return dealsDataElement.getValue();
		}

		return null;
	}

	public static Element getXMLFirstElementByxPath(String xPathStr, Document document) {
		XPathExpression<Element> xPath = XPathFactory.instance().compile(xPathStr, Filters.element());
		Element dataElement = xPath.evaluateFirst(document);

		if (dataElement != null) {
			return dataElement;
		}

		return null;
	}

	/**
	 * Get HTTP Resource and save to DISK
	 * 
	 * @param remoteURI
	 * @param localFile
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static Long saveRemoteFile2Disk(String remoteURI, File localFile) {

		Long fileSize = 0L;

		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpGet httpget = new HttpGet(remoteURI);

		try {
			logger.debug("Get content from URL: [{}]", httpget.getRequestLine());
			CloseableHttpResponse response = httpclient.execute(httpget);

			try {
				HttpEntity entity = response.getEntity();

				if (response.getStatusLine().getStatusCode() >= 400) {
					logger.error("Got bad response, error code = [{}] for imageUrl [{}]", response.getStatusLine().getStatusCode(),
							remoteURI);
				}
				if (entity != null) {
					FileUtils.copyInputStreamToFile(entity.getContent(), localFile);
					fileSize = entity.getContentLength();
				}
			} finally {
				response.close();
			}

		} catch (Exception e) {
			logger.error("Response Error", e);

		}

		return fileSize;
	}

	/**
	 * 
	 * 
	 * @param upcStr
	 * @return
	 */
	public static Integer getUpcCheckDigit(String upcStr) {
		// If upcStr not number then return NULL, this is not UPC
		if (!StringUtils.isNumeric(upcStr)) {
			return null;
		}

		// If upcStr not number AND lenght < 8 then return NULL, this is not UPC
		if (StringUtils.isNumeric(upcStr) && StringUtils.length(upcStr) < 8) {

			return null;
		}

		// UPC, GTIN-8
		if (StringUtils.length(upcStr) == 8) {

			int sumOdd = 0;
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 1, 2));
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 3, 4));
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 5, 6));

			int sumEven = 0;
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 0, 1));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 2, 3));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 4, 5));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 6, 7));
			sumEven = 3 * sumEven;

			int checksum_value = sumOdd + sumEven;
			int checksum_digit = 10 - (checksum_value % 10);

			if (checksum_digit == 10) {
				checksum_digit = 0;
			}

			return checksum_digit;
		}
		// UPC, GTIN-12
		if (StringUtils.length(upcStr) == 12) {

			int sumOdd = 0;
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 1, 2));
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 3, 4));
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 5, 6));
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 7, 8));
			sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr, 9, 10));

			int sumEven = 0;
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 0, 1));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 2, 3));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 4, 5));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 6, 7));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 8, 9));
			sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr, 10, 11));
			sumEven = 3 * sumEven;

			int checksum_value = sumOdd + sumEven;
			int checksum_digit = 10 - (checksum_value % 10);

			if (checksum_digit == 10) {
				checksum_digit = 0;
			}

			return checksum_digit;
		}
		// UPC, GTIN-13
		if (StringUtils.length(upcStr) == 13) {

			try {

				String x = StringUtils.left(upcStr, 12);
				Integer checksum_digit = Integer.valueOf(EAN13CheckDigit.EAN13_CHECK_DIGIT.calculate(x));
				return checksum_digit;
			} catch (CheckDigitException e) {

				logger.debug("Get EAN-13 checksum ERROR" + e);
				return null;
			}

			// int sumOdd = 0;
			// sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr,
			// 1, 2));
			// sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr,
			// 3, 4));
			// sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr,
			// 5, 6));
			// sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr,
			// 7, 8));
			// sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr,
			// 9, 10));
			// sumOdd = sumOdd + Integer.valueOf(StringUtils.substring(upcStr,
			// 11, 12));
			//
			// int sumEven = 0;
			// sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr,
			// 0, 1));
			// sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr,
			// 2, 3));
			// sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr,
			// 4, 5));
			// sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr,
			// 6, 7));
			// sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr,
			// 8, 9));
			// sumEven = sumEven + Integer.valueOf(StringUtils.substring(upcStr,
			// 10, 11));
			// sumEven = 3 * sumEven;
			//
			// int checksum_value = sumOdd + sumEven;
			// int checksum_digit = 10 - (checksum_value % 10);
			//
			// if (checksum_digit == 10) {
			// checksum_digit = 0;
			// }
			//
			// return checksum_digit;
		}

		return null;
	}

	/**
	 * Check the GTIN Code correct nor not
	 * 
	 * @param gtin
	 * @return
	 */
	public static boolean isGTIN(String gtin) {
		// If upcStr not number then return NULL, this is not UPC
		if (!StringUtils.isNumeric(gtin)) {
			return false;
		}

		// UPC, GTIN
		if (StringUtils.length(gtin) == 8 || StringUtils.length(gtin) == 12 || StringUtils.length(gtin) == 13
				|| StringUtils.length(gtin) == 14) {
			try {
				String checksumDigit = StringUtils.right(gtin, 1);
				String eanCode = StringUtils.chop(gtin);
				String checksumDigitCalculated = EAN13CheckDigit.EAN13_CHECK_DIGIT.calculate(eanCode);

				logger.trace("[{}] / [{}]", gtin, checksumDigitCalculated);
				if (StringUtils.equals(checksumDigit, checksumDigitCalculated))
					return true;
			} catch (CheckDigitException e) {
				logger.trace("Get EAN-13 checksum ERROR" + e);
			}
		}
		return false;
	}

	/**
	 * Get price for Float
	 * 
	 * @param priceStr
	 * @return
	 */
	public static Float getPrice(String priceStr) {
		if (StringUtils.isNotBlank(priceStr)) {
			if (StringUtils.contains(priceStr, ".")) {
				return Float.valueOf(priceStr);
			} else {
				return Float.valueOf(Integer.valueOf(priceStr) / 100);
			}
		} else {
			return 0.0f;
		}
	}


	public static BufferedImage cropAndResize(File imageFile, int targetWidth, int targetHeight) {

		ImagePlus imp = IJ.openImage(imageFile.getAbsolutePath());

		ImageProcessor ip = imp.getProcessor();
		System.out.println("size1: " + ip.getWidth() + "x" + ip.getHeight());
		ip.setInterpolationMethod(ImageProcessor.BILINEAR);
		ip = ip.resize(targetWidth * 2, targetHeight * 2);
		System.out.println("size2: " + ip.getWidth() + "x" + ip.getHeight());

		int cropX = ip.getWidth() / 2;
		int cropY = ip.getHeight() / 2;
		ip.setRoi(cropX, cropY, targetWidth, targetHeight);
		ip = ip.crop();
		System.out.println("size3: " + ip.getWidth() + "x" + ip.getHeight());
		BufferedImage croppedImage = ip.getBufferedImage();

		System.out.println("size4: " + ip.getWidth() + "x" + ip.getHeight());
		new ImagePlus("croppedImage", croppedImage).show();

		return croppedImage;

	}

	/**
	 * Get http response as string
	 * 
	 * @param response
	 * @return
	 */
	public static String convertHttpResponse2String(HttpResponse response) {

		logger.debug("Response Code : " + response.getStatusLine().getStatusCode());

		String responseStr = null;

		HttpEntity entity = response.getEntity();

		if (entity != null) {
			// Read content
			try {
				InputStream instream = entity.getContent();

				StringWriter writer = new StringWriter();
				IOUtils.copy(instream, writer, "UTF-8");
				responseStr = writer.toString();
			} catch (Exception e) {
				logger.error("Read http response Error", e);
			}

		}

		// logger.debug("R {}", responseStr);

		return responseStr;

	}

	/**
	 * Get http response as string
	 * 
	 * @param response
	 * @return
	 * @throws IOException
	 */
	public static void writeDom2File(File docFile, Document dom) throws IOException {

		logger.debug("Write Document to disk");

		// new XMLOutputter().output(doc, System.out);
		XMLOutputter xmlOutput = new XMLOutputter();

		// display nice nice
		xmlOutput.setFormat(Format.getPrettyFormat());

		FileUtils.writeStringToFile(docFile, xmlOutput.outputString(dom));
		// System.out.println("SSS" + xmlReques);

	}

	/**
	 * Convert Document to String
	 * 
	 * @param dom
	 * @return
	 * @throws IOException
	 */
	public static String convertDom2String(Document dom) {
		logger.debug("Convert Document to string");

		XMLOutputter xmlOutput = new XMLOutputter();
		xmlOutput.setFormat(Format.getPrettyFormat());

		return xmlOutput.outputString(dom);

	}
}